package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasEsModel;
import com.xbongbong.paas.elasticsearch.model.PaasFormDataEsModel;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.pojo.dto.DeleteDTO;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.help.FormDataListSearchHelp;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.pojo.IdNamePojo;
import com.xbongbong.paas.pojo.KeyValuePojo;
import com.xbongbong.paas.pojo.dto.FormDataListDTO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.service.PaasFormDataService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.FormulaUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.PageHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.customerstatement.pojo.StatementCustomerPojo;
import com.xbongbong.pro.customerstatement.pojo.StatementSummaryPojo;
import com.xbongbong.pro.customerstatement.pojo.StatementTableDataExportPojo;
import com.xbongbong.pro.customerstatement.pojo.StatementTableDataPojo;
import com.xbongbong.pro.customerstatement.pojo.dto.StatementCustomerListDTO;
import com.xbongbong.pro.customerstatement.pojo.dto.StatementCustomerSummaryDTO;
import com.xbongbong.pro.customerstatement.pojo.dto.StatementCustomerTableDTO;
import com.xbongbong.pro.customerstatement.pojo.dto.StatementCustomerTotalDTO;
import com.xbongbong.pro.customerstatement.pojo.vo.StatementCustomerListVO;
import com.xbongbong.pro.customerstatement.pojo.vo.StatementCustomerSummaryVO;
import com.xbongbong.pro.customerstatement.pojo.vo.StatementCustomerTableVO;
import com.xbongbong.pro.customerstatement.pojo.vo.StatementCustomerTotalVO;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.BusinessTypeErrorCodeEnum;
import com.xbongbong.pro.enums.fund.enums.ModelTypeEnum;
import com.xbongbong.pro.service.toolbox.statistic.help.ChartQueryHelp;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.statistic.through.pojo.TableDataInfoPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.PageConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.constant.fund.FundSetConstant;
import com.xbongbong.saas.domain.entity.CustomerStatementEntity;
import com.xbongbong.saas.domain.entity.CustomerStatementProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.ext.OutstockEntityExt;
import com.xbongbong.saas.enums.CustomerStatementDistributorEnum;
import com.xbongbong.saas.enums.CustomerStatementEnum;
import com.xbongbong.saas.enums.CustomerStatementProductEnum;
import com.xbongbong.saas.enums.PaymentSourceTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.product.ContractOutstockProductEnum;
import com.xbongbong.saas.help.FundHelp;
import com.xbongbong.saas.help.SaasListHelp;
import com.xbongbong.saas.model.CustomerStatementModel;
import com.xbongbong.saas.model.CustomerStatementProductModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.OutstockModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.service.CustomerStatementService;
import com.xbongbong.saas.service.toolbox.help.SaasListDataPermissionHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.domain.entity.UserEntity;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * 客户对账单保存
 * @author yuqian
 */
@Service("customerStatementService")
public class CustomerStatementServiceImpl implements CustomerStatementService {
    private static final Logger LOG = LoggerFactory.getLogger(CustomerStatementServiceImpl.class);
    @Resource
    private CustomerStatementModel customerStatementModel;
    @Resource
    private SaasListHelp saasListHelp;
    @Resource
    private SaasListDataPermissionHelp saasListDataPermissionHelp;
    @Resource
    private PaasFormDataService paasFormDataService;
    @Resource
    private EsHelper esHelper;
    @Resource
    private ChartQueryHelp chartQueryHelp;
    @Resource
    private PaasFormDataEsModel paasFormDataEsModel;
    @Resource
    private FundHelp fundHelp;
    @Resource
    private FormDataListSearchHelp formDataListSearchHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private PaasEsModel paasEsModel;
    @Resource
    private OutstockModel outstockModel;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private CustomerStatementProductModel customerStatementProductModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private IndexTypeModel indexTypeModel;

    @Override
    public PaasFormDataEsListVO parentList(FormDataListDTO formDataListDTO) throws XbbException {
        if (!Objects.equals(formDataListDTO.getBusinessType(),XbbRefTypeEnum.CUSTOMER_STATEMENT.getCode())){
            throw new XbbException(BusinessTypeErrorCodeEnum.API_ERROR_361001);
        }
        List<ConditionsEntityExt> conditionList = formDataListDTO.getConditions();
        conditionList.addAll(saasListHelp.packageConditionBySubBusinessType(formDataListDTO.getSubBusinessType(),formDataListDTO.getCorpid(),formDataListDTO.getUserId(), formDataListDTO.getStatusFilter()));
        UserEntity userEntity = new UserEntity();
        BeanUtil.copyProperties(formDataListDTO.getLoginUser(),userEntity);
        saasListDataPermissionHelp.listDataPermission(conditionList,formDataListDTO.getLoginUser(),XbbRefTypeEnum.CUSTOMER_STATEMENT.getCode(), formDataListDTO.getSubBusinessType());
        formDataListDTO.setConditions(conditionList);
        return paasFormDataService.listByEs(formDataListDTO);
    }

    @Override
    public void save(Integer businessType, Map<String, FieldAttrEntity> explainMap, List<? extends PaasFormDataEntityExt> entityList, Integer distributorMark) throws XbbException {
        if(CollectionsUtil.isNotEmpty(entityList)){
            if (Objects.isNull(explainMap)) {
                explainMap = new HashMap<>(PaasConstant.ASSOCIATION_LIST_MAX_SIZE);
            }
            String corpid = entityList.get(0).getCorpid();
            List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
            if(Objects.equals(businessType, XbbRefTypeEnum.PAYMENT.getCode())){
                customerStatementList = insertStatement4Payment(entityList, explainMap);
            }else if(Objects.equals(businessType,XbbRefTypeEnum.PAYMENT_SHEET.getCode())){
                customerStatementList = insertStatement4Sheet(entityList, explainMap);
            }else if(Objects.equals(businessType,XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())){
                customerStatementList = insertStatement4Outstock(entityList, explainMap);
            }
            if (customerStatementList.size() > 0) {
                // 为了加个关联产品，app_id,menu_id,form_id都要保存
                PaasFormEntityExt paasFormEntityExt = paasFormModel.getByBusinessType4Distributor(XbbRefTypeEnum.CUSTOMER_STATEMENT.getCode(), corpid, distributorMark, null);
                for (CustomerStatementEntity customerStatementEntity : customerStatementList) {
                    customerStatementEntity.setAppId(paasFormEntityExt.getAppId());
                    customerStatementEntity.setMenuId(paasFormEntityExt.getMenuId());
                    customerStatementEntity.setFormId(paasFormEntityExt.getId());
                }
                customerStatementModel.insertBatch(customerStatementList);
                List<CustomerStatementProductEntity> productEntityList = new LinkedList<>();
                if (Objects.equals(businessType,XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                    // 保存关联产品相关数据
                    for (CustomerStatementEntity customerStatementEntity : customerStatementList) {
                        JSONObject data = customerStatementEntity.getData();
                        Long sourceId = data.getLong(CustomerStatementEnum.SOURCE_ID.getAttr());
                        if (CollectionsUtil.isNotEmpty(data.getJSONArray(CustomerStatementEnum.PRODUCT.getAttr()))) {
                            // 存在产品时，产品数据单独存在tb_saas_customer_statement_product中
                            OutstockEntityExt outstockEntityExt = outstockModel.getByKey(sourceId, corpid);
                            if (Objects.isNull(outstockEntityExt)) {
                                continue;
                            }
                            List<OutstockProductEntity> outstockProducts = outstockProductModel.getProductsByOutstockIdIn(corpid, Collections.singletonList(outstockEntityExt.getId()));
                            for (OutstockProductEntity outstockProduct : outstockProducts) {
                                CustomerStatementProductEntity productEntity = new CustomerStatementProductEntity(corpid, 0);
                                productEntity.setStatementId(customerStatementEntity.getId());
                                BeanUtil.copyProperties(outstockProduct, productEntity);
                                productEntity.setId(null);
                                productEntityList.add(productEntity);
                            }
                        }
                    }
                    if (CollectionsUtil.isNotEmpty(productEntityList)) {
                        customerStatementProductModel.insertBatch(productEntityList);
                    }
                }
            }
        }
    }

    @Override
    public void insertBatch4Script(boolean dealWithEs, Integer businessType, Map<String, Map<String, FieldAttrEntity>> payExplainMap, Map<String, Integer> payDistributorMarkMap, Map<String, PaasFormExplainEntity> statementExplainMap, List<? extends PaasFormDataEntityExt> entityList) throws XbbException {
        //并不一定是同一个corpid的数据
        if(CollectionsUtil.isEmpty(entityList)){
            return;
        }
        List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
        if(Objects.equals(businessType, XbbRefTypeEnum.PAYMENT.getCode())){
            customerStatementList = insertStatement4PaymentScript(payExplainMap, payDistributorMarkMap, statementExplainMap, entityList);
        }else if(Objects.equals(businessType,XbbRefTypeEnum.PAYMENT_SHEET.getCode())){
            customerStatementList = insertStatement4SheetScript(payExplainMap, payDistributorMarkMap, statementExplainMap, entityList);
        }
        if (customerStatementList.size() > 0) {
            customerStatementModel.insertBatch(dealWithEs, customerStatementList);
        }
    }

    @Override
    public StatementCustomerListVO customerIdList(StatementCustomerListDTO statementCustomerListDTO) throws XbbException {
        StatementCustomerListVO statementCustomerListVO = new StatementCustomerListVO();
        List<IdNamePojo> idNamePojoList = new ArrayList<>();
        statementCustomerListVO.setList(idNamePojoList);
        String corpid = statementCustomerListDTO.getCorpid();
        StatementCustomerPojo statementCustomerPojo = fundHelp.getStatementCustomerPojo(statementCustomerListDTO.getCheckingDate());
        //------第一步，得到对账日期范围内的客户id集合
        Set<Long> customerIdSet = fundHelp.getStatementCustomerIdList(statementCustomerListDTO.getConditions(), corpid, statementCustomerPojo);
        if (CollectionUtils.isEmpty(customerIdSet)) {
            //无对账客户，则返回
            return statementCustomerListVO;
        }
        List<Object> customerIdIn = new ArrayList<>(customerIdSet);
        //------第二步，得到最终对账客户(查客户表)：根据客户更新时间降序，过滤筛选范围（登录员工权限）
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        BeanUtil.copyProperties(statementCustomerListDTO, formDataListDTO, true);
        List<String> fieldList = Arrays.asList(BasicConstant.ID, CustomerManagementEnum.NAME.getAttr());
        //把查询客户列表的相关参数、及fieldList等信息塞入
        saasListHelp.getFormDataListDTO4Statement(formDataListDTO, customerIdIn, fieldList);
        //查询客户
        IndexTypeEnum indexTypeEnum = indexTypeModel.getIndexType(formDataListDTO.getCorpid(), formDataListDTO.getBusinessType(), formDataListDTO.getSaasMark());
        PaasFormDataEsListVO paasFormDataEsListVO = paasFormDataEsModel.list(formDataListDTO, indexTypeEnum);
        List<PaasFormDataEntityExt> customerList = paasFormDataEsListVO.getPaasFormDataESList();
        for (PaasFormDataEntityExt entityExt : customerList) {
            String name = JsonHelperUtil.getJSONObject(entityExt.getData()).getString(CustomerManagementEnum.NAME.getAttr());
            idNamePojoList.add(new IdNamePojo(entityExt.getId(), name));
        }
        statementCustomerListVO.setPageHelper(paasFormDataEsListVO.getPageHelper());
        return statementCustomerListVO;
    }

    @Override
    public StatementCustomerTableVO customerTable(StatementCustomerTableDTO statementCustomerTableDTO) throws XbbException {
        StatementCustomerTableVO statementCustomerTableVO = new StatementCustomerTableVO();
        String corpid = statementCustomerTableDTO.getCorpid();
        Integer distributorMark = statementCustomerTableDTO.getDistributorMark();
        StatementCustomerPojo statementCustomerPojo = fundHelp.getStatementCustomerPojo(statementCustomerTableDTO.getCheckingDate(), statementCustomerTableDTO.getCustomerId(), null);
        //获取应收款-回款类型的 下拉框items
        Map<Object, String> itemsMap = fundHelp.getItemsFromPaymentExplain(corpid, distributorMark);
        //------对账单表查询条件封装
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.statementQuery4Table(corpid, statementCustomerPojo);
        //排序：checkingDate asc，id asc
        List<SortBuilder> sortBuilderList = Arrays.asList(new FieldSortBuilder(CustomerStatementEnum.getEsAttr(CustomerStatementEnum.CHECKING_DATE)).order(SortOrder.ASC), new FieldSortBuilder(BasicConstant.ID).order(SortOrder.ASC));
        // 限制返回值
        List<String> partFieldList = new ArrayList<>(CustomerStatementEnum.getEsAttrList(Arrays.asList(CustomerStatementEnum.CHECKING_DATE,  CustomerStatementEnum.TYPE, CustomerStatementEnum.AMOUNT_RECEIVABLE, CustomerStatementEnum.AMOUNT_RECEIVED, CustomerStatementEnum.WRITE_OFF_AMOUNT)));
        partFieldList.add("data." + CustomerStatementEnum.STATEMENT_NO.getAttr());
        partFieldList.add("data." + CustomerStatementEnum.CONTRACT_NO.getAttr());
        partFieldList.add("data." + CustomerStatementEnum.SOURCE_NO.getAttr());
        partFieldList.add("data." + CustomerStatementEnum.PRODUCT.getAttr());
        partFieldList.add("data." + CustomerStatementEnum.OTHER_EXPENSE.getAttr());
        partFieldList.add(StringConstant.CORPID);
        partFieldList.add(BusinessConstant.ID);
        partFieldList.add(CustomerStatementEnum.SYS_LONG_1.getAttr());
        partFieldList.add(CustomerStatementEnum.SYS_LONG_2.getAttr());
        boolean exportFlag = Objects.equals(statementCustomerTableDTO.getFromExport(), BasicConstant.ONE);
        // 对账流水是否有产品信息
        boolean containProduct = false;
        //时间已经校验过
        Long firstTime = statementCustomerTableDTO.getCheckingDate().get(0);
        String beginTime = DateTimeUtil.getStringEpochSecond(firstTime, DateTimeUtil.SDFDate);
        String beginName = I18nMessageUtil.getMessage(FundSetConstant.BEGINNING);
        if (exportFlag) {
            //导出的时候需要返回一下对账单id
            List<CustomerStatementEntity> list = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT, boolQueryBuilder, CustomerStatementEntity.class, partFieldList, sortBuilderList);
            //按照对账日期升序排
            list = list.stream().sorted(Comparator.comparingLong(item -> item.getData().getLongValue(CustomerStatementEnum.CHECKING_DATE.getAttr()))).collect(Collectors.toList());
            List<StatementTableDataExportPojo> exportData = new ArrayList<>();
            //第一个塞入期初
            double beginAmount = fundHelp.getStatementBegining(corpid, statementCustomerPojo);
            String beginAmountStr = StringUtil.formatDouble(beginAmount);
            exportData.add(new StatementTableDataExportPojo(beginTime, I18nMessageUtil.getMessage(FundSetConstant.BEGINNING), beginAmountStr));
            //格式化，并封装非期初信息（应收款、回款单）到dataStr内
            double balanceReceivable = beginAmount;
            //计算表格内三个字段的总金额：
            Double amountReceivable = BasicConstant.ZERO_DOUBLE;
            Double amountReceived = BasicConstant.ZERO_DOUBLE;
            Double writeOffAmount = BasicConstant.ZERO_DOUBLE;
            //生成对账单添加回款类型
            Map<Long, String> paymentIdMap = fundHelp.getPaymentIdMapForStatementData(list, corpid,itemsMap);

            for (CustomerStatementEntity entity : list) {
                if (CollectionsUtil.isNotEmpty(entity.getData().getJSONArray(CustomerStatementEnum.PRODUCT.getAttr()))) {
                    containProduct = true;
                }
                Integer businessType = entity.getSysLong1();
                Long statemetId = entity.getSysLong2();
                balanceReceivable = formatTableData4Export(balanceReceivable, entity, exportData, 0,paymentIdMap,businessType,statemetId);
                JSONObject data = entity.getData();
                amountReceivable = Arith.add(amountReceivable, data.getDoubleValue(CustomerStatementEnum.AMOUNT_RECEIVABLE.getAttr()));
                amountReceived = Arith.add(amountReceived, data.getDoubleValue(CustomerStatementEnum.AMOUNT_RECEIVED.getAttr()));
                writeOffAmount = Arith.add(writeOffAmount, data.getDoubleValue(CustomerStatementEnum.WRITE_OFF_AMOUNT.getAttr()));
            }
            //塞入合计/剩余
            String totalAmountReceivableStr = fundHelp.formatMoneyStr(amountReceivable);
            String totalAmountReceivedStr = fundHelp.formatMoneyStr(amountReceived);
            String totalWriteOffAmountStr = fundHelp.formatMoneyStr(writeOffAmount);
            String totalBalanceReceivableStr = fundHelp.formatMoneyStr(balanceReceivable);
            //总应收余额转换为大写的中文金额
            String totalBalanceReceivableChinese = FormulaUtil.upperMoneyToChinese(totalBalanceReceivableStr);
            exportData.add(new StatementTableDataExportPojo(I18nMessageUtil.getMessage(I18nStringConstant.STATEMENT_TOTAL_REMAINING), totalAmountReceivableStr, totalAmountReceivedStr, totalWriteOffAmountStr, totalBalanceReceivableStr, totalBalanceReceivableChinese));
            statementCustomerTableVO.setExportData(exportData);
            //对账表-表头
            statementCustomerTableVO.setTitle(packageTitle(containProduct, distributorMark));
            // 处理列表页字段排序
            formDataListSearchHelp.explainFieldSort4CustomerStatement(statementCustomerTableDTO, statementCustomerTableVO.getTitle());
        } else {
            List<String> fieldList = new ArrayList<>(partFieldList);
            fieldList.add(CustomerStatementEnum.SYS_LONG_1.getAttr());
            fieldList.add(CustomerStatementEnum.SYS_LONG_2.getAttr());
            fieldList.add("data." + CustomerStatementEnum.CONTRACT_ID.getAttr());
            fieldList.add("data." + CustomerStatementEnum.SOURCE_TYPE.getAttr());
            fieldList.add("data." + CustomerStatementEnum.SOURCE_ID.getAttr());
            fieldList.addAll(partFieldList);
            Integer page = statementCustomerTableDTO.getPage();
            Integer pageSize = statementCustomerTableDTO.getPageSize();
            XbbAggregatedPage<CustomerStatementEntity> aggregatedPage = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT, boolQueryBuilder, CustomerStatementEntity.class, fieldList, page, pageSize, sortBuilderList);
            int rowCounts = 0;
            List<CustomerStatementEntity> list = new ArrayList<>();
            if (aggregatedPage != null) {
                rowCounts = (int) aggregatedPage.getTotalElements();
                list = aggregatedPage.getContent();
            }
            PageHelper pageHelper = PageHelperUtil.initPageHelper(rowCounts, page, pageSize, PageConstant.DEFAULT_PAGE_SIZE);
            List<StatementTableDataPojo> data = new ArrayList<>();
            double beginAmount = 0D;
            if (Objects.equals(page, BasicConstant.ONE)) {
                //只有第一页才需要第一个塞入期初（但总数还是要加上期初的）
                beginAmount = fundHelp.getStatementBegining(corpid, statementCustomerPojo);
                String beginAmountStr = StringUtil.formatDouble(beginAmount);
                StatementTableDataPojo beginStatementTableDataPojo = new StatementTableDataPojo(
                        new TableDataInfoPojo(beginTime),
                        new TableDataInfoPojo(beginName),
                        new TableDataInfoPojo(beginAmountStr));
                data.add(beginStatementTableDataPojo);
            } else if (page > BasicConstant.ONE && list.size() > BasicConstant.ZERO) {
                //如果是第一页之后且有数据的，则要计算第一页之前的应付款余额
                CustomerStatementEntity customerStatementEntity = list.get(0);
                //当页第一个账户的期初
                beginAmount = getFirstBalanceReceivable(corpid, customerStatementEntity, statementCustomerPojo);
            }
            //生成对账单添加回款类型
            Map<Long, String> paymentIdMap = fundHelp.getPaymentIdMapForStatementData(list, corpid, itemsMap);
            //格式化，并封装非期初信息（应收款、回款单）到data内
            double balanceReceivable = beginAmount;
            for (CustomerStatementEntity entity : list) {
                if (CollectionsUtil.isNotEmpty(entity.getData().getJSONArray(CustomerStatementEnum.PRODUCT.getAttr()))) {
                    containProduct = true;
                }
                Integer businessType = entity.getSysLong1();
                Long statemetId = entity.getSysLong2();
                balanceReceivable = formatTableData(corpid, balanceReceivable, entity, data, 0,paymentIdMap,businessType,statemetId);
            }
            //对账表-表头
            statementCustomerTableVO.setTitle(packageTitle(containProduct, distributorMark));
            statementCustomerTableVO.setData(data);
            statementCustomerTableVO.setPageHelper(pageHelper);
            // 处理列表页字段排序
            formDataListSearchHelp.explainFieldSort4CustomerStatement(statementCustomerTableDTO, statementCustomerTableVO.getTitle());
        }
        return statementCustomerTableVO;
    }

    @Override
    public StatementCustomerSummaryVO customerSummary(StatementCustomerSummaryDTO statementCustomerSummaryDTO) throws XbbException {
        String corpid = statementCustomerSummaryDTO.getCorpid();
        StatementCustomerPojo statementCustomerPojo = fundHelp.getStatementCustomerPojo(statementCustomerSummaryDTO.getCheckingDate(), statementCustomerSummaryDTO.getCustomerId(), null);
        //------期初计算
        double beginAmount = fundHelp.getStatementBegining(corpid, statementCustomerPojo);
        //------非期初（整个表格）汇总：corpid、del、checkingDate(开始、结束)、客户id
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.statementQuery4Table(corpid, statementCustomerPojo);
        String amountReceivableAttr = CustomerStatementEnum.getEsAttr(CustomerStatementEnum.AMOUNT_RECEIVABLE);
        String amountReceivedAttr = CustomerStatementEnum.getEsAttr(CustomerStatementEnum.AMOUNT_RECEIVED);
        String writeOffAmountAttr = CustomerStatementEnum.getEsAttr(CustomerStatementEnum.WRITE_OFF_AMOUNT);
        List<String> sumFieldList = Arrays.asList(amountReceivableAttr, amountReceivedAttr, writeOffAmountAttr);
        //一次性汇总应收金额、回款金额、核销金额
        JSONObject result = esHelper.sumMuchField4Some(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT, boolQueryBuilder, sumFieldList);
        double totalAmountReceivable = result.getDoubleValue(amountReceivableAttr);
        double totalAmountReceived = result.getDoubleValue(amountReceivedAttr);
        double totalWriteOffAmount = result.getDoubleValue(writeOffAmountAttr);
        //总应收余额 = 期初 + 总应收金额 - 总核销金额
        double totalBalanceReceivable = Arith.sub(Arith.add(beginAmount, totalAmountReceivable), totalWriteOffAmount);
        String totalAmountReceivableStr = fundHelp.formatMoneyStr(totalAmountReceivable);
        String totalAmountReceivedStr = fundHelp.formatMoneyStr(totalAmountReceived);
        String totalWriteOffAmountStr = fundHelp.formatMoneyStr(totalWriteOffAmount);
        String totalBalanceReceivableStr = fundHelp.formatMoneyStr(totalBalanceReceivable);
        //总应收余额转换为大写的中文金额
        String totalBalanceReceivableChinese = FormulaUtil.upperMoneyToChinese(totalBalanceReceivableStr);
        StatementSummaryPojo summary = new StatementSummaryPojo(totalAmountReceivableStr, totalAmountReceivedStr, totalWriteOffAmountStr, totalBalanceReceivableStr);
        StatementSummaryPojo total = new StatementSummaryPojo(totalBalanceReceivableStr, totalBalanceReceivableChinese);
        return new StatementCustomerSummaryVO(summary, total);
    }

    @Override
    public StatementCustomerTotalVO customerTotal(StatementCustomerTotalDTO statementCustomerTotalDTO) throws XbbException {
        StatementCustomerTotalVO statementCustomerTotalVO = new StatementCustomerTotalVO();
        StatementCustomerPojo statementCustomerPojo = fundHelp.getStatementCustomerPojo(statementCustomerTotalDTO.getCheckingDate());
        String corpid = statementCustomerTotalDTO.getCorpid();
        //------第一步，得到对账日期范围内的客户id集合
        Set<Long> customerIdSet = fundHelp.getStatementCustomerIdList(statementCustomerTotalDTO.getConditions(), corpid, statementCustomerPojo);
        if (CollectionUtils.isEmpty(customerIdSet)) {
            //无对账客户，则返回
            return statementCustomerTotalVO;
        }
        List<Object> customerIdIn = new ArrayList<>(customerIdSet);
        //------第二步，得到最终对账客户(查客户表)：过滤筛选范围（登录员工权限）
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        BeanUtil.copyProperties(statementCustomerTotalDTO, formDataListDTO, true);
        List<String> fieldList = Collections.singletonList(BasicConstant.ID);
        //把查询客户列表的相关参数、及fieldList等信息塞入
        saasListHelp.getFormDataListDTO4Statement(formDataListDTO, customerIdIn, fieldList);
        //置为导出，因为要汇总所有数据
        formDataListDTO.setFromExport(BasicConstant.ONE);
        //查询客户
        IndexTypeEnum indexTypeEnum = indexTypeModel.getIndexType(formDataListDTO.getCorpid(), formDataListDTO.getBusinessType(), formDataListDTO.getSaasMark());
        PaasFormDataEsListVO paasFormDataEsListVO = paasFormDataEsModel.list(formDataListDTO, indexTypeEnum);
        List<PaasFormDataEntityExt> customerList = paasFormDataEsListVO.getPaasFormDataESList();
        List<Long> customerIdList = customerList.stream().map(item -> item.getId()).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(customerIdList)) {
            //无对账客户，则返回
            return statementCustomerTotalVO;
        }
        //------第三步，得到截至到筛选截至日期的总应收、总核销及总应收，则该应收即为当前客户的所有总应收。因此把截止时间置入startTime内，按照期初的计算方式聚合计算即可
        statementCustomerPojo.setStartTime(statementCustomerPojo.getEndTime());
        statementCustomerPojo.setCustomerIdList(customerIdList);
        //最后计算所有客户的总应收余额，与期初的计算方式一致
        double amount = fundHelp.getStatementBegining(corpid, statementCustomerPojo);
        String totalBalanceReceivableStr = fundHelp.formatMoneyStr(amount);
        //总应收余额转换为大写的中文金额
        String totalBalanceReceivableChinese = FormulaUtil.upperMoneyToChinese(totalBalanceReceivableStr);
        StatementSummaryPojo total = new StatementSummaryPojo(totalBalanceReceivableStr, totalBalanceReceivableChinese);
        statementCustomerTotalVO.setTotal(total);
        return statementCustomerTotalVO;
    }

    @Override
    public Integer deleteBatch(String corpid, Integer businessType, List<Long> sheetIdIn) throws XbbException {
        if (CollectionUtils.isEmpty(sheetIdIn)) {
            return BasicConstant.ZERO;
        }
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termQuery(CustomerStatementEnum.SYS_LONG_1.getAttr(), businessType));
        List<CustomerStatementEntity> result = formHelp.getFormListByIds4All(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT,boolQueryBuilder,CustomerStatementEnum.SYS_LONG_2.getAttr(), sheetIdIn, Collections.singletonList(BasicConstant.ID), CustomerStatementEntity.class);
        if (CollectionUtils.isNotEmpty(result)) {
            result.forEach(e -> {
                e.setCorpid(corpid);
                e.setDel(DelEnum.DELETE.getDel());
                e.setUpdateTime(DateTimeUtil.getInt());
            });
            return customerStatementModel.updateBatch(result, corpid);
        }
        return BasicConstant.ONE;
    }

    @Override
    public void addRollback(String corpid, Integer businessType, List<Long> sheetIdIn) throws XbbException {
        if (CollectionUtils.isEmpty(sheetIdIn)) {
            return;
        }
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termQuery(CustomerStatementEnum.SYS_LONG_1.getAttr(), businessType));
        List<CustomerStatementEntity> result = formHelp.getFormListByIds4All(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT,boolQueryBuilder, CustomerStatementEnum.SYS_LONG_2.getAttr(), sheetIdIn, Collections.singletonList(BasicConstant.ID), CustomerStatementEntity.class);
        if (CollectionUtils.isNotEmpty(result)) {
            //新建失败，则直接删除es
            List<DeleteDTO> deleteUserList = new ArrayList<>();
            for (CustomerStatementEntity entity : result) {
                DeleteDTO deleteDTO = new DeleteDTO();
                deleteDTO.setEsId(corpid + "_" + entity.getId());
                deleteDTO.setIndexTypeEnum(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT);
                deleteUserList.add(deleteDTO);
            }
            paasEsModel.physicalDeleteBatchChild(deleteUserList, corpid, IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT);
        }
    }

    @Override
    public void updateRollback(String corpid, Integer businessType, Collection<Long> sheetIdIn) throws XbbException {
        if (Objects.isNull(businessType) || CollectionUtils.isEmpty(sheetIdIn)) {
            return ;
        }
        //从数据库读取数据，同步至es
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(StringConstant.CORPID, corpid);
        params.put("businessType", businessType);
        params.put("statementIdIn", sheetIdIn);
        List<CustomerStatementEntity> result = customerStatementModel.findEntitys(params);
        if (CollectionUtils.isNotEmpty(result)) {
            customerStatementModel.updateBatchEs(result);
        }
    }

    @Override
    public void dataConsistencyUpdateCustomer(String corpid, Long customerId, String customerName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(CustomerStatementEnum.getEsAttr(CustomerStatementEnum.CUSTOMER_ID), customerId));
            List<String> fieldList = Collections.singletonList(BasicConstant.ID);
            List<CustomerStatementEntity> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT, filter, CustomerStatementEntity.class, fieldList);
            if (CollectionUtils.isEmpty(esEntities)) {
                return;
            }
            Set<Long> ids = esEntities.stream().map(CustomerStatementEntity::getId).collect(Collectors.toSet());
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<CustomerStatementEntity> entityList = customerStatementModel.findEntitys(param);
            if (CollectionUtils.isNotEmpty(entityList)) {
                long now = DateTimeUtil.getInt();
                for (CustomerStatementEntity entity : entityList) {
                    JSONObject data = entity.getData();
                    data.put(CustomerStatementEnum.CUSTOMER_NAME.getAttr(), customerName);
                    entity.setData(data);
                    entity.setUpdateTime(now);
                }
                customerStatementModel.updateBatch(entityList, corpid);
            }

        } catch (Exception e) {
            LOG.error("CustomerStatementServiceImpl.dataConsistencyUpdateCustomer 出错，corpid=" + corpid + "  customerId=" + customerId + " customerName=" + customerName, e);
        }
    }

    @Override
    public void dataConsistencyUpdateContract(String corpid, Long contractId, String contractNo) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(CustomerStatementEnum.getEsAttr(CustomerStatementEnum.CONTRACT_ID), contractId));
            List<String> fieldList = Collections.singletonList(BasicConstant.ID);
            List<CustomerStatementEntity> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT, filter, CustomerStatementEntity.class, fieldList);
            if (CollectionUtils.isEmpty(esEntities)) {
                return;
            }
            Set<Long> ids = esEntities.stream().map(CustomerStatementEntity::getId).collect(Collectors.toSet());
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<CustomerStatementEntity> entityList = customerStatementModel.findEntitys(param);
            if (CollectionUtils.isNotEmpty(entityList)) {
                long now = DateTimeUtil.getInt();
                for (CustomerStatementEntity entity : entityList) {
                    JSONObject data = entity.getData();
                    data.put(CustomerStatementEnum.CONTRACT_NO.getAttr(), contractNo);
                    entity.setData(data);
                    entity.setUpdateTime(now);
                }
                customerStatementModel.updateBatch(entityList, corpid);
            }

        } catch (Exception e) {
            LOG.error("CustomerStatementServiceImpl.dataConsistencyUpdateContract 出错，corpid=" + corpid + "   contractId=" + contractId + "  contractNo=" + contractNo, e);
        }
    }

    /**
     * 应收款进入对账单，插入list封装
     * @param entityList
     * @return java.util.List<com.xbongbong.saas.domain.entity.CustomerStatementEntity>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<CustomerStatementEntity> insertStatement4Payment(List<? extends PaasFormDataEntityExt> entityList, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
        String timeAttr = PaymentEnum.ESTIMATE_TIME.getAttr();
        FieldAttrEntity timeAttrEntity = explainMap.get(timeAttr);
        for(PaasFormDataEntityExt paasFormDataEntity : entityList){
            if (Objects.equals(paasFormDataEntity.getDel(), DelEnum.DELETE.getDel())) {
                continue;
            }
            JSONObject data = paasFormDataEntity.getData();
            JSONObject customerStatementData = new JSONObject();
            CustomerStatementEntity customerStatementEntity = getCustomerStatementByPayment(timeAttr, timeAttrEntity, paasFormDataEntity, data, customerStatementData);
            customerStatementEntity.setData(customerStatementData);
            customerStatementList.add(customerStatementEntity);
        }
        return customerStatementList;
    }

    /**
     * 脚本：应收款进入对账单，插入list封装
     * @param payExplainMap key为corpid_formId；value为explainMap（只有时间字段，如回款时间、预计回款时间，因为插入对账单的对账日期需要用到）
     * @param payDistributorMarkMap key为corpid_formId；value为distributorMark
     * @param statementExplainMap 对账单 key为corpid_distributorMark；value为PaasFormExplainEntity（对账单记录需要存对账单的appId、menuId、formId）
     * @param entityList 应收款list，并不一定是同一个corpid的数据
     * @return java.util.List<com.xbongbong.saas.domain.entity.CustomerStatementEntity>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<CustomerStatementEntity> insertStatement4PaymentScript(Map<String, Map<String, FieldAttrEntity>> payExplainMap, Map<String, Integer> payDistributorMarkMap, Map<String, PaasFormExplainEntity> statementExplainMap, List<? extends PaasFormDataEntityExt> entityList) throws XbbException {
        List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
        String timeAttr = PaymentEnum.ESTIMATE_TIME.getAttr();
        for(PaasFormDataEntityExt paasFormDataEntity : entityList){
            if (Objects.equals(paasFormDataEntity.getDel(), DelEnum.DELETE.getDel())) {
                continue;
            }
            //corpid_formId，用于读取应收款的explainMap和distributorMark
            String key = String.format(SymbolConstant.UNDERLINE_PLACE, paasFormDataEntity.getCorpid(), paasFormDataEntity.getFormId());
            Integer distributorMark = payDistributorMarkMap.get(key);
            //corpid_distributorMark，用于读取对账单的explainEntity
            String keyStatement = String.format(SymbolConstant.UNDERLINE_PLACE, paasFormDataEntity.getCorpid(), distributorMark);
            PaasFormExplainEntity statementExplainEntity = statementExplainMap.get(keyStatement);
            if (Objects.isNull(statementExplainEntity)) {
                continue;
            }
            Map<String, FieldAttrEntity> explainMap = payExplainMap.get(key);
            FieldAttrEntity timeAttrEntity = Objects.nonNull(explainMap) ? explainMap.get(timeAttr) : null;
            JSONObject data = paasFormDataEntity.getData();
            JSONObject customerStatementData = new JSONObject();
            CustomerStatementEntity customerStatementEntity = getCustomerStatementByPayment(timeAttr, timeAttrEntity, paasFormDataEntity, data, customerStatementData);
            customerStatementEntity.setData(customerStatementData);
            //设置appId等
            customerStatementEntity.setAppId(statementExplainEntity.getAppId());
            customerStatementEntity.setMenuId(statementExplainEntity.getMenuId());
            customerStatementEntity.setFormId(statementExplainEntity.getId());
            customerStatementList.add(customerStatementEntity);
        }
        return customerStatementList;
    }

    /**
     * 回款单进入对账单，插入list封装
     * @param entityList
     * @return java.util.List<com.xbongbong.saas.domain.entity.CustomerStatementEntity>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<CustomerStatementEntity> insertStatement4Sheet(List<? extends PaasFormDataEntityExt> entityList, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
        String timeAttr = PaymentSheetEnum.PAYMENT_TIME.getAttr();
        FieldAttrEntity timeAttrEntity = explainMap.get(timeAttr);
        for(PaasFormDataEntityExt paasFormDataEntity : entityList){
            if (Objects.equals(paasFormDataEntity.getDel(), DelEnum.DELETE.getDel())) {
                continue;
            }
            JSONObject customerStatementData = new JSONObject();
            CustomerStatementEntity customerStatementEntity = getCustomerStatementBySheet(timeAttr, timeAttrEntity, paasFormDataEntity, customerStatementData);
            customerStatementEntity.setData(customerStatementData);
            customerStatementList.add(customerStatementEntity);
        }
        return customerStatementList;
    }

    /**
     * 脚本：应收款进入对账单，插入list封装
     * @param payExplainMap key为corpid_formId；value为explainMap（只有时间字段，如回款时间、预计回款时间，因为插入对账单的对账日期需要用到）
     * @param payDistributorMarkMap key为corpid_formId；value为distributorMark
     * @param statementExplainMap 对账单 key为corpid_distributorMark；value为PaasFormExplainEntity（对账单记录需要存对账单的appId、menuId、formId）
     * @param entityList 应收款list，并不一定是同一个corpid的数据
     * @return java.util.List<com.xbongbong.saas.domain.entity.CustomerStatementEntity>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<CustomerStatementEntity> insertStatement4SheetScript(Map<String, Map<String, FieldAttrEntity>> payExplainMap, Map<String, Integer> payDistributorMarkMap, Map<String, PaasFormExplainEntity> statementExplainMap, List<? extends PaasFormDataEntityExt> entityList) throws XbbException {
        List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
        String timeAttr = PaymentSheetEnum.PAYMENT_TIME.getAttr();
        for(PaasFormDataEntityExt paasFormDataEntity : entityList){
            if (Objects.equals(paasFormDataEntity.getDel(), DelEnum.DELETE.getDel())) {
                continue;
            }
            //corpid_formId，用于读取回款单的explainMap和distributorMark
            String key = String.format(SymbolConstant.UNDERLINE_PLACE, paasFormDataEntity.getCorpid(), paasFormDataEntity.getFormId());
            Integer distributorMark = payDistributorMarkMap.get(key);
            //corpid_distributorMark，用于读取对账单的explainEntity
            String keyStatement = String.format(SymbolConstant.UNDERLINE_PLACE, paasFormDataEntity.getCorpid(), distributorMark);
            PaasFormExplainEntity statementExplainEntity = statementExplainMap.get(keyStatement);
            if (Objects.isNull(statementExplainEntity)) {
                continue;
            }
            Map<String, FieldAttrEntity> explainMap = payExplainMap.get(key);
            FieldAttrEntity timeAttrEntity = Objects.nonNull(explainMap) ? explainMap.get(timeAttr) : null;
            JSONObject customerStatementData = new JSONObject();
            CustomerStatementEntity customerStatementEntity = getCustomerStatementBySheet(timeAttr, timeAttrEntity, paasFormDataEntity, customerStatementData);
            customerStatementEntity.setData(customerStatementData);
            //设置appId等
            customerStatementEntity.setAppId(statementExplainEntity.getAppId());
            customerStatementEntity.setMenuId(statementExplainEntity.getMenuId());
            customerStatementEntity.setFormId(statementExplainEntity.getId());
            customerStatementList.add(customerStatementEntity);
        }
        return customerStatementList;
    }

    /**
     * 按出库模式产生的对账流水
     * @param entityList 虽然是出库产生应收，但是本质还是用出库生成的应收款来生成对账单，即该list为应收款list
     * @param explainMap 应收款解释
     * @return
     */
    private List<CustomerStatementEntity> insertStatement4Outstock(List<? extends PaasFormDataEntityExt> entityList, Map<String, FieldAttrEntity> explainMap) throws XbbException{
        List<CustomerStatementEntity> customerStatementList = new ArrayList<>();
        String timeAttr = PaymentEnum.ESTIMATE_TIME.getAttr();
        FieldAttrEntity timeAttrEntity = explainMap.get(timeAttr);
        for(PaasFormDataEntityExt paasFormDataEntity : entityList){
            if (Objects.equals(paasFormDataEntity.getDel(), DelEnum.DELETE.getDel())) {
                continue;
            }
            JSONObject data = paasFormDataEntity.getData();
            JSONObject customerStatementData = new JSONObject();
            CustomerStatementEntity customerStatementEntity = getCustomerStatementByPayment(timeAttr, timeAttrEntity, paasFormDataEntity, data, customerStatementData);
            //处理其它费用，产品
            Double otherExpense = FastJsonHelper.getDoubleFromFormData(data, BasicConstant.STATEMENT_OTHER_EXPENSE);
            customerStatementData.put(CustomerStatementEnum.OTHER_EXPENSE.getAttr(), otherExpense);
            JSONArray products = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.STATEMENT_PRODUCT,new JSONArray());
            JSONArray idIn = new JSONArray();
            for (int i = 0, size=products.size(); i < size; i++) {
                JSONObject product = products.getJSONObject(i);
                idIn.add(product.getString(ContractOutstockProductEnum.PRODUCT.getAttr()));
            }
            customerStatementData.put(CustomerStatementEnum.PRODUCT.getAttr(), idIn);
            customerStatementEntity.setData(customerStatementData);
            customerStatementList.add(customerStatementEntity);
        }
        return customerStatementList;
    }

    /**
     * 通过应收款生成的对账单
     * （1）直接生成应收款时，再生成对账单，比如新建红冲应收款时
     * （2）根据应收规则(手动创建应收/按开票产生应收/按出库产生应收)生成应收款后，再基于应收款生成对账单，而不是用来源单据本身直接生成对账单流水
     * @param timeAttr 应收款的预计回款日期字段attr
     * @param timeAttrEntity 应收款的预计回款日期字段实体
     * @param paasFormDataEntity 应收款实体
     * @param data 应收款data
     * @param customerStatementData 对账单流水data
     * @return com.xbongbong.saas.domain.entity.CustomerStatementEntity
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private CustomerStatementEntity getCustomerStatementByPayment(String timeAttr, FieldAttrEntity timeAttrEntity, PaasFormDataEntityExt paasFormDataEntity, JSONObject data, JSONObject customerStatementData) {
        CustomerStatementEntity customerStatementEntity = new CustomerStatementEntity();
        customerStatementEntity.setCorpid(paasFormDataEntity.getCorpid());
        customerStatementData.put(CustomerStatementEnum.STATEMENT_ID.getAttr(), paasFormDataEntity.getId());
        customerStatementData.put(CustomerStatementEnum.STATEMENT_NO.getAttr(), paasFormDataEntity.getSerialNo());
        Long modelType = paasFormDataEntity.getSysLong10();
        //实体的创建时间
        Long addTime = Objects.isNull(paasFormDataEntity.getAddTime()) ? DateTimeUtil.getInt() : paasFormDataEntity.getAddTime();
        if (Objects.isNull(modelType)) {
            modelType = ModelTypeEnum.PLAN.getCode();
        }
        customerStatementEntity.setSysLong10(modelType);
        customerStatementEntity.setAddTime(addTime);
        customerStatementEntity.setUpdateTime(addTime);
        customerStatementEntity.setSysLong1(XbbRefTypeEnum.PAYMENT.getCode());
        customerStatementEntity.setSysLong2(paasFormDataEntity.getId());
        customerStatementEntity.setDel(DelEnum.NORMAL.getDel());
        Long sheetTime = addTime;
        try {
            sheetTime = fundHelp.getSheetTime4FlowAndStatement(data, timeAttr, timeAttrEntity, addTime);
        } catch (Exception e) {
            LOG.error("error payment Id :"+paasFormDataEntity.getId()+"addTime error" + e, e);
        }
        customerStatementData.put(CustomerStatementEnum.CHECKING_DATE.getAttr(), sheetTime);
        customerStatementData.put(CustomerStatementEnum.BUSINESS_TYPE.getAttr(), XbbRefTypeEnum.PAYMENT.getCode());
        customerStatementData.put(CustomerStatementEnum.CUSTOMER_ID.getAttr(), data.getLongValue(PaymentEnum.CUSTOMER.getAttr()));
        customerStatementData.put(CustomerStatementEnum.CUSTOMER_NAME.getAttr(), data.getString(PaymentEnum.CUSTOMER_LINK_TEXT.getAttr()) == null ? "" : data.getString(PaymentEnum.CUSTOMER_LINK_TEXT.getAttr()));
        customerStatementData.put(CustomerStatementEnum.CONTRACT_ID.getAttr(), data.getLongValue(PaymentEnum.CONTRACT.getAttr()));
        customerStatementData.put(CustomerStatementEnum.CONTRACT_NO.getAttr(), data.getString(PaymentEnum.CONTRACT_LINK_TEXT.getAttr()) == null ? "" : data.getString(PaymentEnum.CONTRACT_LINK_TEXT.getAttr()));
        //手动创建应收时来源单据为空
        Integer sourceRefType = data.getInteger(PaymentEnum.SOURCE_REF_TYPE.getAttr());
        if (Objects.nonNull(sourceRefType) && sourceRefType > 0) {
            customerStatementData.put(CustomerStatementEnum.SOURCE_TYPE.getAttr(), sourceRefType);
            customerStatementData.put(CustomerStatementEnum.SOURCE_ID.getAttr(), data.getLong(PaymentEnum.SOURCE.getAttr()));
            customerStatementData.put(CustomerStatementEnum.SOURCE_NO.getAttr(), data.getString(PaymentEnum.SOURCE_LINK_TEXT.getAttr()));
        }
        customerStatementData.put(CustomerStatementEnum.AMOUNT_RECEIVABLE.getAttr(), data.getDoubleValue(PaymentEnum.AMOUNT.getAttr()));
        return customerStatementEntity;
    }

    /**
     * 通过回款单生成的对账单
     * @param timeAttr 回款单的回款日期字段attr
     * @param timeAttrEntity 回款单回款日期字段实体
     * @param paasFormDataEntity 回款单实体
     * @param customerStatementData 对账单流水data
     * @return com.xbongbong.saas.domain.entity.CustomerStatementEntity
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private CustomerStatementEntity getCustomerStatementBySheet(String timeAttr, FieldAttrEntity timeAttrEntity, PaasFormDataEntityExt paasFormDataEntity, JSONObject customerStatementData) throws XbbException {
        CustomerStatementEntity customerStatementEntity = new CustomerStatementEntity();
        customerStatementEntity.setCorpid(paasFormDataEntity.getCorpid());
        customerStatementData.put(CustomerStatementEnum.STATEMENT_ID.getAttr(),paasFormDataEntity.getId());
        customerStatementData.put(CustomerStatementEnum.STATEMENT_NO.getAttr(),paasFormDataEntity.getSerialNo());
        Long modelType = paasFormDataEntity.getSysLong10();
        if (Objects.isNull(modelType)) {
            modelType = ModelTypeEnum.PLAN.getCode();
        }
        customerStatementEntity.setSysLong10(modelType);
        //实体的创建时间
        Long addTime = Objects.isNull(paasFormDataEntity.getAddTime()) ? DateTimeUtil.getInt() : paasFormDataEntity.getAddTime();
        customerStatementEntity.setAddTime(addTime);
        customerStatementEntity.setUpdateTime(addTime);
        customerStatementEntity.setSysLong1(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
        customerStatementEntity.setSysLong2(paasFormDataEntity.getId());
        customerStatementEntity.setDel(DelEnum.NORMAL.getDel());
        JSONObject data = paasFormDataEntity.getData();
        Long sheetTime = addTime;
        try {
            sheetTime = fundHelp.getSheetTime4FlowAndStatement(data, timeAttr, timeAttrEntity, addTime);
        } catch (Exception e) {
            LOG.error("error sheet Id :"+paasFormDataEntity.getId()+"time error" + e, e);
        }
        customerStatementData.put(CustomerStatementEnum.CHECKING_DATE.getAttr(), sheetTime);
        customerStatementData.put(CustomerStatementEnum.BUSINESS_TYPE.getAttr(), XbbRefTypeEnum.PAYMENT_SHEET.getCode());
        customerStatementData.put(CustomerStatementEnum.TYPE.getAttr(), data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr()));
        customerStatementData.put(CustomerStatementEnum.CUSTOMER_ID.getAttr(), data.getLongValue(PaymentSheetEnum.CUSTOMER_ID.getAttr()));
        customerStatementData.put(CustomerStatementEnum.CUSTOMER_NAME.getAttr(), FastJsonHelper.getStringOrDefaultFromFormData(data,PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr(),""));

        JSONArray contractArray = FastJsonHelper.getJsonArrOrDefaultFromFormData(data,PaymentSheetEnum.CONTRACT.getAttr(),new JSONArray());
        JSONArray contractLinkArray = FastJsonHelper.getJsonArrOrDefaultFromFormData(data,PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(),new JSONArray());
        if(contractArray.size() > BasicConstant.ZERO){
            customerStatementData.put(CustomerStatementEnum.CONTRACT_ID.getAttr(), contractArray.getLong(BasicConstant.ZERO));
        }
        if(contractLinkArray.size() > BasicConstant.ZERO){
            customerStatementData.put(CustomerStatementEnum.CONTRACT_NO.getAttr(), contractLinkArray.getString(BasicConstant.ZERO));
        }
        //回款金额
        customerStatementData.put(CustomerStatementEnum.AMOUNT_RECEIVED.getAttr(), data.getDoubleValue(PaymentSheetEnum.AMOUNT.getAttr()));
        //核销金额
        customerStatementData.put(CustomerStatementEnum.WRITE_OFF_AMOUNT.getAttr(), data.getDoubleValue(PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr()));
        return customerStatementEntity;
    }

    /**
     * 得到当页第一条对账记录之前的所有记录总的balanceReceivable(应收余额)：应收总金额 - 核销总金额
     * @param corpid 公司id
     * @param pageFirstStatementEntity 当页第一条对账记录实体
     * @param statementCustomerPojo 对账单时间、客户id参数pojo
     * @return double
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private double getFirstBalanceReceivable(String corpid, CustomerStatementEntity pageFirstStatementEntity, StatementCustomerPojo statementCustomerPojo) throws XbbException {
        /*
         ①得到当前页第一条数据的checkingDate及其customerStatementId（客户对账记录id）；
         ②聚合得到 小于checkingDate or 等于checkingDate且id小于customerStatementId的总sheetAmount
            a、不能直接用小于等于checkingDate且对账记录id小于customerStatementId，因为customerStatementId并不是直接排序的关键字段，只是作为辅助字段进行排序（当checkingDate一致时，用customerStatementId进行排序）
            b、处理该步需保证对账记录排序是按照 checkingDate asc，id asc；如果不是该排序，则计算其他对账记录的余额时加减做相应处理即可
            c、汇总两个字段的值：应收金额、核销金额
         ③该页其他数据直接用上一个账户余额，±对账记录应收金额、核销金额计算得到即可。
         */
        Long customerStatementId = pageFirstStatementEntity.getId();
        Long checkingDate = pageFirstStatementEntity.getData().getLong(CustomerStatementEnum.CHECKING_DATE.getAttr());
        /*--------处理查询条件--------*/
        BoolQueryBuilder beforeQueryBuilder = chartQueryHelp.statementQuery4PageFirstBefore(corpid, checkingDate, customerStatementId, statementCustomerPojo);
        //对应②c
        double firstBalanceReceivable = fundHelp.getStatementTotalBalanceReceivable(beforeQueryBuilder, IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT);
        //最后③在该方法外部基于该方法返回值计算即可
        return firstBalanceReceivable;
    }

    @Override
    public double formatTableData(String corpid, double balanceReceivable, PaasFormDataEntity entity, List<StatementTableDataPojo> dataList, Integer snapshot,Map<Long,String> paymentIdMap,Integer businessType,Long statementId) {
        JSONObject data = entity.getData();
        String checkDataStr = DateTimeUtil.getStringEpochSecond(data.getLong(CustomerStatementEnum.CHECKING_DATE.getAttr()), DateTimeUtil.SDFDate);
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        String businessTypeStr = xbbRefTypeEnum.getName();
        String statementNo = data.getString(CustomerStatementEnum.STATEMENT_NO.getAttr());
        Long contractId = data.getLong(CustomerStatementEnum.CONTRACT_ID.getAttr());
        String contractNo = data.getString(CustomerStatementEnum.CONTRACT_NO.getAttr());
        String sourceNo = data.getString(CustomerStatementEnum.SOURCE_NO.getAttr());
        Long sourceId = data.getLong(CustomerStatementEnum.SOURCE_ID.getAttr());
        String type = data.getString(CustomerStatementEnum.TYPE.getAttr());
        String customerName = data.getString(CustomerStatementEnum.CUSTOMER_NAME.getAttr());
        Long customerId = data.getLong(CustomerStatementEnum.CUSTOMER_ID.getAttr());
        Integer sourceType = data.getInteger(CustomerStatementEnum.SOURCE_TYPE.getAttr());
        StatementTableDataPojo statementTableDataPojo = new StatementTableDataPojo();
        statementTableDataPojo.setDataId(entity.getDataId());
        statementTableDataPojo.setFormId(entity.getFormId());
        statementTableDataPojo.setCheckingDate(new TableDataInfoPojo(checkDataStr));
        statementTableDataPojo.setContractId(new TableDataInfoPojo(customerName, BasicConstant.ONE, XbbRefTypeEnum.CUSTOMER.getCode(), customerId));
        statementTableDataPojo.setContractId(new TableDataInfoPojo(contractNo, BasicConstant.ONE, XbbRefTypeEnum.CONTRACT.getCode(), contractId));
        //期初不入库，因此对账单库里面都为应收款或回款单
        if (Objects.equals(businessType, XbbRefTypeEnum.PAYMENT.getCode())) {
            statementTableDataPojo.setBusinessType(new TableDataInfoPojo(businessTypeStr));
            statementTableDataPojo.setPaymentType(new TableDataInfoPojo(paymentIdMap.get(statementId)));
            statementTableDataPojo.setStatementId(new TableDataInfoPojo(statementNo, BasicConstant.ONE, XbbRefTypeEnum.PAYMENT.getCode(), statementId));
            //自建应收是没有来源单据的，无需封装
            if (Objects.nonNull(sourceId) && sourceId > 0) {
                statementTableDataPojo.setSourceId(new TableDataInfoPojo(sourceNo, BasicConstant.ONE, sourceType, sourceId));
            }
            Double amountReceivable = data.getDouble(CustomerStatementEnum.AMOUNT_RECEIVABLE.getAttr());
            statementTableDataPojo.setAmountReceivable(new TableDataInfoPojo(StringUtil.formatDouble(amountReceivable)));
            //应收余额 = 期初 + 应收款 - 核销金额
            balanceReceivable = Arith.add(balanceReceivable, amountReceivable);
            if (CollectionsUtil.isNotEmpty(data.getJSONArray(CustomerStatementEnum.PRODUCT.getAttr())) && Objects.equals(sourceType, PaymentSourceTypeEnum.OUTSTOCK.getBusinessType())) {
                JSONObject product = new JSONObject();
                // 找到对账流水对应的产品数据
                List<CustomerStatementProductEntity> productEntityList = customerStatementProductModel.getByStatementId(corpid, entity.getId(), snapshot);
                if (CollectionsUtil.isNotEmpty(productEntityList) && Objects.nonNull(productEntityList.get(0))) {
                    CustomerStatementProductEntity productEntity = productEntityList.get(0);
                    product.put(SelectProductEnum.PRODUCT_NAME.getAttr(), productEntity.getProductName());
                    product.put(SelectProductEnum.PRODUCT_NO.getAttr(), productEntity.getProductNo());
                    product.put(SelectProductEnum.SPECIFICATION.getAttr(), getSpec(productEntity.getProductSpecification()));
                    product.put(CustomerStatementProductEnum.SELLING_PRICE.getAttr(), productEntity.getProductPrice());
                    product.put(CustomerStatementProductEnum.NUM.getAttr(), productEntity.getProductNum());
                    double subtotal = 0D;
                    if (Objects.nonNull(productEntity.getProductPrice()) && Objects.nonNull(productEntity.getProductNum())) {
                        subtotal = Arith.mul(productEntity.getProductPrice(), productEntity.getProductNum());
                    }
                    product.put(CustomerStatementProductEnum.SUBTOTAL.getAttr(), subtotal);
                    statementTableDataPojo.setArray_1(product);
                    statementTableDataPojo.setOtherExpense(new TableDataInfoPojo(StringUtil.formatDouble(data.getDouble(CustomerStatementEnum.OTHER_EXPENSE.getAttr()))));
                }
            }
        } else if (Objects.equals(businessType, XbbRefTypeEnum.PAYMENT_SHEET.getCode())) {
            PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByCode(type);
            String sheetType = String.format(SymbolConstant.BRACKETS_ENGLISH, paymentSheetTypeEnum.getName());
            statementTableDataPojo.setBusinessType(new TableDataInfoPojo(businessTypeStr + sheetType));
            statementTableDataPojo.setStatementId(new TableDataInfoPojo(statementNo, BasicConstant.ONE, XbbRefTypeEnum.PAYMENT_SHEET.getCode(), statementId));
            Double amountReceived = data.getDouble(CustomerStatementEnum.AMOUNT_RECEIVED.getAttr());
            Double writeOffAmount = data.getDouble(CustomerStatementEnum.WRITE_OFF_AMOUNT.getAttr());
            statementTableDataPojo.setAmountReceived(new TableDataInfoPojo(StringUtil.formatDouble(amountReceived)));
            statementTableDataPojo.setWriteOffAmount(new TableDataInfoPojo(StringUtil.formatDouble(writeOffAmount)));
            //应收余额 = 期初 + 应收款 - 核销金额
            balanceReceivable = Arith.sub(balanceReceivable, writeOffAmount);
        }
        statementTableDataPojo.setBalanceReceivable(new TableDataInfoPojo(StringUtil.formatDouble(balanceReceivable)));
        dataList.add(statementTableDataPojo);
        return balanceReceivable;
    }

    /**
     * 获取规格
     * @param spec
     * @return
     * @throws XbbException
     */
    public String getSpec(String spec){
        StringBuilder stringBuilder = new StringBuilder();
        JSONObject specifition;
        try {
            specifition = JSONObject.parseObject(spec);
        } catch (Exception e) {
            return spec;
        }
        if (Objects.nonNull(specifition)){
            specifition.forEach((key,value)->  StringUtil.stringBuilderAppendSuffix(stringBuilder,"/",value));
        }
        return stringBuilder.toString();
    }

    /**
     * 格式化并封装非期初信息（应收款、回款单）
     * @param balanceReceivable 期初应收余额，用于逐条计算各行的应收余额
     * @param entity 对账单实体
     * @param exportDataList 最终返回的数据list
     * @return double
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @Override
    public double formatTableData4Export(double balanceReceivable, PaasFormDataEntity entity, List<StatementTableDataExportPojo> exportDataList, Integer snapshot,Map<Long,String> paymentIdMap,Integer businessType,Long statementId) {
        JSONObject data = entity.getData();
        String checkDataStr = DateTimeUtil.getStringEpochSecond(data.getLong(CustomerStatementEnum.CHECKING_DATE.getAttr()), DateTimeUtil.SDFDate);
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        String businessTypeStr = xbbRefTypeEnum.getName();
        String statementNo = data.getString(CustomerStatementEnum.STATEMENT_NO.getAttr());
        String contractNo = data.getString(CustomerStatementEnum.CONTRACT_NO.getAttr());
        String sourceNo = data.getString(CustomerStatementEnum.SOURCE_NO.getAttr());
        //导出的直接封装字段文字，不用封装为对象
        StatementTableDataExportPojo statementTableDataExportPojo;
        //期初不入库，因此对账单库里面都为应收款或回款单
        if (Objects.equals(businessType, XbbRefTypeEnum.PAYMENT.getCode())) {
            Double amountReceivable = data.getDouble(CustomerStatementEnum.AMOUNT_RECEIVABLE.getAttr());
            //应收余额 = 期初 + 应收款 - 核销金额
            balanceReceivable = Arith.add(balanceReceivable, amountReceivable);
            statementTableDataExportPojo = new StatementTableDataExportPojo(checkDataStr, businessTypeStr, statementNo, contractNo, sourceNo, StringUtil.formatDouble(amountReceivable), null, null, StringUtil.formatDouble(balanceReceivable),paymentIdMap.get(statementId));
            if (CollectionsUtil.isNotEmpty(data.getJSONArray(CustomerStatementEnum.PRODUCT.getAttr()))) {
                JSONArray products = new JSONArray();
                // 找到对账流水对应的产品数据
                List<CustomerStatementProductEntity> productEntityList = customerStatementProductModel.getByStatementId(entity.getCorpid(), entity.getId(), snapshot);
                for (CustomerStatementProductEntity productEntity : productEntityList) {
                    JSONObject product = new JSONObject();
                    product.put(SelectProductEnum.PRODUCT_NAME.getAttr(), productEntity.getProductName());
                    product.put(SelectProductEnum.PRODUCT_NO.getAttr(), productEntity.getProductNo());
                    product.put(SelectProductEnum.SPECIFICATION.getAttr(), getSpec(productEntity.getProductSpecification()));
                    product.put(CustomerStatementProductEnum.SELLING_PRICE.getAttr(), productEntity.getProductPrice());
                    product.put(CustomerStatementProductEnum.NUM.getAttr(), productEntity.getProductNum());
                    double subtotal = 0D;
                    if (Objects.nonNull(productEntity.getProductPrice()) && Objects.nonNull(productEntity.getProductNum())) {
                        subtotal = Arith.mul(productEntity.getProductPrice(), productEntity.getProductNum());
                    }
                    product.put(CustomerStatementProductEnum.SUBTOTAL.getAttr(), subtotal);
                    products.add(product);
                }
                statementTableDataExportPojo.setArray_1(products);
                statementTableDataExportPojo.setOtherExpense(StringUtil.formatDouble(data.getDouble(CustomerStatementEnum.OTHER_EXPENSE.getAttr())));
            }
        } else {
            Double amountReceived = data.getDouble(CustomerStatementEnum.AMOUNT_RECEIVED.getAttr());
            Double writeOffAmount = data.getDouble(CustomerStatementEnum.WRITE_OFF_AMOUNT.getAttr());
            //应收余额 = 期初 + 应收款 - 核销金额
            balanceReceivable = Arith.sub(balanceReceivable, writeOffAmount);
            PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByCode(data.getString(CustomerStatementEnum.TYPE.getAttr()));
            String sheetType = String.format(SymbolConstant.BRACKETS_ENGLISH, paymentSheetTypeEnum.getName());
            statementTableDataExportPojo = new StatementTableDataExportPojo(checkDataStr, businessTypeStr + sheetType, statementNo, contractNo, sourceNo, null, StringUtil.formatDouble(amountReceived), StringUtil.formatDouble(writeOffAmount), StringUtil.formatDouble(balanceReceivable),null);
        }
        exportDataList.add(statementTableDataExportPojo);
        return balanceReceivable;
    }

    /**
     * 封装对账单表头list
     * @param containProduct 包含产品
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public static List<KeyValuePojo> packageTitle(boolean containProduct, Integer distributorMark) {
        //对账表-表头
        CustomerStatementEnum enumCheckingData = CustomerStatementEnum.CHECKING_DATE;
        CustomerStatementEnum enumBusinessType = CustomerStatementEnum.BUSINESS_TYPE;
        CustomerStatementEnum enumStatementId = CustomerStatementEnum.STATEMENT_ID;
        CustomerStatementEnum enumContractId = CustomerStatementEnum.CONTRACT_ID;
        String contractName = Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode()) ? CustomerStatementDistributorEnum.CONTRACT_ID.getAttrName() : CustomerStatementEnum.CONTRACT_ID.getAttrName();
        CustomerStatementEnum enumSourceId = CustomerStatementEnum.SOURCE_ID;
        CustomerStatementEnum enumAmountReceivable = CustomerStatementEnum.AMOUNT_RECEIVABLE;
        CustomerStatementEnum enumAmountReceived = CustomerStatementEnum.AMOUNT_RECEIVED;
        CustomerStatementEnum enumWriteOffAmount = CustomerStatementEnum.WRITE_OFF_AMOUNT;
        CustomerStatementEnum enumBalanceReceivable = CustomerStatementEnum.BALANCE_RECEIVABLE;
        CustomerStatementEnum paymentType = CustomerStatementEnum.PAYMENT_TYPE;
        List<KeyValuePojo> title = new ArrayList<>(Arrays.asList(
                new KeyValuePojo("checkingDate", enumCheckingData.getAttrName(), enumCheckingData.getAttr()),
                new KeyValuePojo("businessType", enumBusinessType.getAttrName(), enumBusinessType.getAttr()),
                new KeyValuePojo("statementId", enumStatementId.getAttrName(), enumStatementId.getAttr()),
                new KeyValuePojo("contractId", contractName, enumContractId.getAttr()),
                new KeyValuePojo("sourceId", enumSourceId.getAttrName(), enumSourceId.getAttr())));
        if (containProduct) {
            List<KeyValuePojo> productTitle = Arrays.asList(
                    new KeyValuePojo(SelectProductEnum.PRODUCT_NAME.getAttr(), SelectProductEnum.PRODUCT_NAME.getAttrName()),
                    new KeyValuePojo(SelectProductEnum.PRODUCT_NO.getAttr(), SelectProductEnum.PRODUCT_NO.getAttrName()),
                    new KeyValuePojo(SelectProductEnum.SPECIFICATION.getAttr(), SelectProductEnum.SPECIFICATION.getAttrName()),
                    new KeyValuePojo(CustomerStatementProductEnum.SELLING_PRICE.getAttr(), CustomerStatementProductEnum.SELLING_PRICE.getAttrName()),
                    new KeyValuePojo(CustomerStatementProductEnum.NUM.getAttr(), CustomerStatementProductEnum.NUM.getAttrName()),
                    new KeyValuePojo(CustomerStatementProductEnum.SUBTOTAL.getAttr(), CustomerStatementProductEnum.SUBTOTAL.getAttrName()));
            KeyValuePojo product = new KeyValuePojo(CustomerStatementEnum.PRODUCT.getAttr(), CustomerStatementEnum.PRODUCT.getAttrName(), CustomerStatementEnum.PRODUCT.getAttr());
            product.setProducts(productTitle);
            title.add(product);
            title.add(new KeyValuePojo("otherExpense", CustomerStatementEnum.OTHER_EXPENSE.getAttrName(), CustomerStatementEnum.OTHER_EXPENSE.getAttr()));
        }
        title.add(new KeyValuePojo("amountReceivable", enumAmountReceivable.getAttrName(), enumAmountReceivable.getAttr()));
        title.add(new KeyValuePojo("amountReceived", enumAmountReceived.getAttrName(), enumAmountReceived.getAttr()));
        title.add(new KeyValuePojo("writeOffAmount", enumWriteOffAmount.getAttrName(), enumWriteOffAmount.getAttr()));
        title.add(new KeyValuePojo("balanceReceivable", enumBalanceReceivable.getAttrName(), enumBalanceReceivable.getAttr()));
        title.add(new KeyValuePojo("paymentType", paymentType.getAttrName(), paymentType.getAttr()));
        return title;
    }
}
